/*************************************************************************
* Nuvoton Electronics Corporation confidential
*
* Copyright (c) 2008 by Nuvoton Electronics Corporation
* All rights reserved
* 
* FILENAME
*   kpi.c
*
* VERSION
*   2.0
*
* DESCRIPTION
*   The kpi device library of Nuvoton ARM9 MCU
*
* DATA STRUCTURES
*   KPICONF_union
*   KPI3KCONF_union
*   KPILPCONF_union
*   KPISTATUS_union
*
* FUNCTIONS
*   KPI_CheckEmptyQue
*   KPI_CheckFullQue
*   KPI_Close
*   KPI_Deque
*   KPI_Enque
*   KPI_Exit
*   KPI_Init
*   KPI_Ioctl
*   KPI_ISR
*   KPI_Open
*   KPI_Read
*
* HISTORY
*   06/24/08    Ver 1.0 Created
*
* REMARK
*
****************************************************************************/

/***************************************************************************
  HISTORY
  2007-01-09
  1. kpiIoctl
  	 Add CONTINUOUS_MODE command for continuous mode support
  	 Step 1: Set GPIO as input mode
  	 Step 2: Set KPI prescale = 0
  	 Step 3: Delay for KPI scan
  	 Step 4: Restore prescale value
  	 Step 5: Set GPIO as KPI function

  2. KPICONF_VALUE
     Modify from 0xXXXX05FA to 0xXXXX35FF for continuous mode support
***************************************************************************/

/***************************************************************************
  HISTORY
  2007-11-30
  1. Rename API function 
  2. Support to 910
  3. Remove intial define 
  4. Remove GPIO configure 
***************************************************************************/
#ifdef ECOS
#include "stdio.h"
#include "drv_api.h"
#include "diag.h"
#include "wbtypes.h"
#include "wbio.h"
#else
#include "wblib.h"
#endif
#include "nuc900_kpi.h"
#include "nuc900_reg.h"

#ifdef ECOS
#define	Successful  	0
#define	Fail        	1
#define sysprintf		diag_printf
#define sysPrintf		diag_printf
#define sysGetTicks(TIMER0)  cyg_current_time()
#endif

//define by project
//#define __WB_ICV_PORT25__
//#define __WB_ICV_PORT6__
//#define _EVB_BD_
//#define _POSTAX_BD_

//#define DEBUG_MODE
//#define _EVB_BD_
#if 0 
	#ifdef __WB_ICV_PORT25__
	#define KPI_1PORT
	#define GPIO_CFG        0xFFF83020
	#define GPIO_CFG_MASK   0xFFF00000
	#define GPIO_CFG_VALUE  0x000AAAAA
	#define KPI_2PORT
	#define GPIO2_CFG       0xFFF83050
	#define GPIO2_CFG_MASK  0xFFF0FFFF
	#define GPIO2_CFG_VALUE 0x000F0000
	#define KPICONF_VALUE   0x000735FF
	#endif

	#ifdef __WB_ICV_PORT6__
	#define KPI_1PORT
	#define GPIO_CFG        0xFFF83060
	#define GPIO_CFG_MASK   0xFF000000
	#define GPIO_CFG_VALUE  0x00AAAAAA
	#define KPICONF_VALUE   0x000F35FF
	#endif

	#ifdef _EVB_BD_
	#define KPI_1PORT
	#define GPIO_CFG        0xFFF83020
	#define GPIO_CFG_MASK   0xFFF00000
	#define GPIO_CFG_VALUE  0x000AAAAA
	#define KPICONF_VALUE   0x000435FF
	#endif

	#ifdef _POSTAX_BD_
	#define KPI_1PORT
	#define GPIO_CFG        0xFFF83060
	#define GPIO_CFG_MASK   0xFFFF00C0
	#define GPIO_CFG_VALUE  0x0000AA2A
	#define KPICONF_VALUE   0x002D20FA
	#endif

#endif

#ifdef _EVB_BD_
	#define KPICONF_VALUE   0x000C20FA
#endif

#ifdef __WB_EVB_EXT_ENCODE_ON___
	#define KPICONF_VALUE   0x003E20FA
#endif

#ifdef __WB_EVB_EXT__ENCODE_OFF____
	#define KPICONF_VALUE   0x003E20FA
#endif

#define NOKEY           0
#define MAX_KPI_BUFFER_SIZE     10
#define KPI_QUE_SIZE            (MAX_KPI_BUFFER_SIZE+1)

#ifdef ECOS
#define IRQ_KPI   			29
cyg_interrupt  kpi_interrupt;
cyg_handle_t   kpi_irq_handle;
#endif


//Internal function definition
static VOID KPI_Enque(CONST UINT uValue);
static UINT KPI_Deque(VOID);
static BOOL KPI_CheckFullQue(VOID);
static BOOL KPI_CheckEmptyQue(VOID);

//Global variable
static INT volatile nKPIBufferRear = 0;  //Queue parameter
static INT volatile nKPIBufferFront = 0; //Queue parameter
static KPISTATUS_union kpiBuffer[KPI_QUE_SIZE]; // kpi buffer(queue)
static BOOL bIskpiOpen=FALSE;
static INT	usePortC = 0;

/****************************************************************************
* FUNCTION
*   kpiISR
*
* DESCRIPTION
*   The interrupt service routines of KPI(including POS demo board)
*
* CALLED BY
*   sysIrqHandler
*
* INPUTS
*   pvParam     IRQ Parameter(not use in KPI)
*
* OUTPUTS
*   None
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLlu	12/22/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
#ifdef ECOS
static cyg_uint32 KPI_ISR(cyg_vector_t vector, cyg_addrword_t data)
{
  	KPI_Enque((UINT)inpw(REG_KPI_STATUS));
	cyg_drv_interrupt_acknowledge(IRQ_KPI);
	return CYG_ISR_HANDLED;
}
#else
VOID KPI_ISR(PVOID pvParam)
{
  	KPI_Enque((UINT)inpw(REG_KPI_STATUS));
	#ifdef DEBUG_MODE
	sysprintf("ISR\n");
    #endif
}
#endif

/****************************************************************************
* FUNCTION
*   kpiInit
*
* DESCRIPTION
*   The init function of KPI device library
*
* CALLED BY
*   various components
*
* INPUTS
*   None
*
* OUTPUTS
*   None
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/22/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
INT KPI_Init(VOID)
{
    
#if 0
	UINT32 nRegisterValue=0;
    #ifdef KPI_1PORT
    nRegisterValue=inpw(GPIO_CFG);
    nRegisterValue=nRegisterValue&GPIO_CFG_MASK;
    nRegisterValue=nRegisterValue|GPIO_CFG_VALUE;
    outpw(GPIO_CFG,nRegisterValue);
    #endif

    #ifdef KPI_2PORT
    nRegisterValue=inpw(GPIO2_CFG);
    nRegisterValue=nRegisterValue&GPIO2_CFG_MASK;
    nRegisterValue=nRegisterValue|GPIO2_CFG_VALUE;
    outpw(GPIO2_CFG,nRegisterValue);
    #endif
#endif
    //Enable KPI System CLK    REG_CLKEN[25]
    UINT32 uReg;
    uReg=inpw(REG_CLKEN)|1<<25;
    outpw(REG_CLKEN,uReg);
     
    //Reset all register
    outpw(REG_KPI_CONF, 0);
    outpw(REG_KPI_3KCONF, 0);
    outpw(REG_KPI_LPCONF, 0);
    outpw(REG_KPI_STATUS, 0);

    //outpw(REG_GPIO_DIR2,0x03FF0000);//Port 2 internal pull-up

    //Set kpi conf default value
    outpw(REG_KPI_CONF, KPICONF_VALUE);

#ifdef ECOS
	outpw(REG_AIC_SCR29, 0xC1);
	cyg_drv_interrupt_create(IRQ_KPI, 8, 0, KPI_ISR, 0,
							&kpi_irq_handle, &kpi_interrupt);
    cyg_drv_interrupt_attach(kpi_irq_handle);
    //cyg_drv_interrupt_unmask(IRQ_KPI);
#else		
    sysSetInterruptType(IRQ_KPI, POSITIVE_EDGE_TRIGGER);
	sysInstallISR(IRQ_LEVEL_1, IRQ_KPI, (PVOID)KPI_ISR);
	sysSetLocalInterrupt(ENABLE_IRQ);  // Enable CPSR I bit
	sysEnableInterrupt(IRQ_KPI);
#endif
	return 0;
}

/****************************************************************************
* FUNCTION
*   kpiExit
*
* DESCRIPTION
*   The exit function of KPI device library
*
* CALLED BY
*   various components
*
* INPUTS
*   None
*
* OUTPUTS
*   None
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/22/05	Created initial version 1.0
*
* REMARK
*     For future usage
****************************************************************************/
INT KPI_Exit(VOID)
{
    return 0;
}

/****************************************************************************
* FUNCTION
*   kpiOpen
*
* DESCRIPTION
*   The open function of KPI device library
*
* CALLED BY
*   various components
*
* INPUTS
*   //nDevIdentity    KPI Identity(not use in NON OS environment)
*
* OUTPUTS
*   Successful
*   kpiBusy
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/22/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
//INT kpiOpen(CONST INT nDevIdentity)
INT KPI_Open(KPI_PORT uCmd)
{
	UINT32 uReg;
    if(bIskpiOpen==TRUE)
    {
        return kpiBusy;
    }
#ifdef ECOS
    cyg_drv_interrupt_unmask(IRQ_KPI);
#else    
    sysEnableInterrupt(IRQ_KPI);
#endif
    bIskpiOpen=TRUE;
    nKPIBufferFront = nKPIBufferRear = 0;
    
    uReg = readw(REG_MFSEL);
	
	/* set GPIO MFSEL */    
	
	switch(uCmd)
	{  
		
	  	
		case PORTI: 
		{ 
		 	  //uReg = uReg|1<<26; 
		 	  uReg = (uReg & 0xf0ffffff)|0x00000000;
		 	  writew(REG_MFSEL,uReg);
		 	  
		 	  break;
		} 	  
	 	  	  
		case PORTC: 	
		{
		  	  //uReg = (uReg|1<<3)&0<<2;
		  	  uReg = (uReg & ~0xC) | 0x8;
		  	  
		  	  writew(REG_MFSEL,uReg);
		  	  usePortC = 1;
		  	  outpw(REG_KPI_CONF, KPICONF_VALUE & ~0x80000);
		 		
		 	  break;
		} 
		default:
        {
            return kpiInvalidIoctlCommand;
            break;
        }  	  
		  	  
		  	
		//writew(REG_MFSEL,uReg);
		
		sysprintf("MFSEL=0x%x",inpw(REG_MFSEL));
	}			
    
    
    #ifdef DEBUG_MODE
	    sysPrintf("KPICONF:0x%08x", inpw(REG_KPI_CONF));
   		sysPrintf("Set GPIOI \n");
    	sysPrintf("Set GPIOC \n");
    	sysPrintf("MFSEL=0x%x\n",readw(REG_MFSEL)); 
    #endif
    return Successful;
}

/****************************************************************************
* FUNCTION
*   kpiClose
*
* DESCRIPTION
*   The close function of KPI device library
*
* CALLED BY
*   various components
*
* INPUTS
*   nDevIdentity    KPI Identity(not use in NON OS environment)
*
* OUTPUTS
*   Successful
*   kpiNotOpen
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/22/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
//INT kpiClose(CONST INT nDevIdentity)
INT KPI_Close(VOID)
{
    if(bIskpiOpen==FALSE)
    {
        return kpiNotOpen;
    }
#ifdef ECOS
    cyg_drv_interrupt_mask(IRQ_KPI);
#else    
    sysDisableInterrupt(IRQ_KPI);
#endif
    bIskpiOpen=FALSE;
    return Successful;
}

/****************************************************************************
* FUNCTION
*   kpiRead
*
* DESCRIPTION
*   The read function of KPI device library
*
* CALLED BY
*   various components
*
* INPUTS
*   nDevIdentity    KPI Identity(not use in NON OS environment)
*   key             The point of struct KPISTATUS_union
*   uReadMode       BLOCK_MODE/ NONBLOCK_MODE
*
* OUTPUTS
*   Successful
*   kpiNotOpen          KPI not open
*   kpiReadModeError    KPI read mode error
*   kpiNoKey            queue is empty, no key press
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/22/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
//INT kpiRead(CONST INT nDevIdentity, PUCHAR pucKey, CONST UINT uReadMode)
INT KPI_Read(PUCHAR pucKey, CONST UINT uReadMode)
{
    if(bIskpiOpen==FALSE)
    {
        return kpiNotOpen;
    }
    switch(uReadMode)
    {
        case BLOCK_MODE:
        {
            while(KPI_CheckEmptyQue() == TRUE);
            ((KPISTATUS_union *)pucKey)->value=KPI_Deque();
            break;
        }
        case NONBLOCK_MODE:
        {
            ((KPISTATUS_union *)pucKey)->value=KPI_Deque();
            break;
        }
        default:
        {
            return kpiReadModeError;
            break;
        }
    }
    if(((KPISTATUS_union *)pucKey)->value == NOKEY)
    {
        return kpiNoKey;
    }
    return Successful;
}

/****************************************************************************
* FUNCTION
*   kpiIoctl
*
* DESCRIPTION
*   The ioctl function of KPI device library
*
* CALLED BY
*   various components
*
* INPUTS
*   nDevIdentity    KPI Identity(not use in NON OS environment)
*   uCommand        Ioctl command which indicates different operation
*   uIndication     Parameter for future usage
*   uValue          The new value which should be writed to register
*                   KPICONF, KPI3KCONF, and KPILPCONF
*
* OUTPUTS
*   Successful
*   kpiNotOpen                  KPI not open
*   kpiInvalidIoctlCommand      Ioctl command error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/22/05	Created initial version 1.0
*
* REMARK
*     None
***************************************************************************/ 
INT KPI_Ioctl(CONST UINT uCommand, CONST UINT uIndication, UINT uValue)
{
    //UINT32 nRegisterValue=0;
    if(bIskpiOpen==FALSE)
    {
        return kpiNotOpen;
    }
    switch(uCommand)
    {
        case SET_KPICONF:
        {
            outpw(REG_KPI_CONF, uValue);
            break;
        }
        case SET_KPI3KCONF:
        {
            outpw(REG_KPI_3KCONF, uValue);
            break;
        }
        case SET_KPILPCONF:
        {
            #ifdef POSKPI //Set LPWCEN as 0xFF under POS demo board
            //uValue=uValue&0xFFFF00FF;
            uValue=uValue|0x0000FF00;
            #endif
            outpw(REG_KPI_LPCONF, uValue);
            break;
        }
        case CLEAN_KPI_BUFFER:
        {
            nKPIBufferFront = nKPIBufferRear = 0;
            break;
        }
        case CONTINUOUS_MODE:
        {
		#if 0
		    // Set GPIO as input mode
		    #ifdef KPI_1PORT
		    nRegisterValue=inpw(GPIO_CFG);
		    nRegisterValue=nRegisterValue&GPIO_CFG_MASK;
		    outpw(GPIO_CFG,nRegisterValue);
		    #endif

		    #ifdef KPI_2PORT
		    nRegisterValue=inpw(GPIO2_CFG);
		    nRegisterValue=nRegisterValue&GPIO2_CFG_MASK;
		    outpw(GPIO2_CFG,nRegisterValue);
		    #endif
        #endif
    		UINT32 volatile btime;
    		// Set KPI prescale = 0
    		if(usePortC)
		    	outpw(REG_KPI_CONF, inpw(REG_KPI_CONF)&0xFFF7FF00);
		    else
		    	outpw(REG_KPI_CONF, inpw(REG_KPI_CONF)&0xFFFFFF00);	

	    	if(uValue == 0)
	    	{
	    		uValue = 1; // Delay at least  10 ms (should be 20us for KPI scan timing)
	    	}

	    	// Delay for KPI scan
	    	//sysDelay( uValue);
#ifndef ECOS
			sysStartTimer(TIMER0, 1, PERIODIC_MODE);
#endif		    
		    btime = sysGetTicks(TIMER0);
		    while (1)
		    {
		        if ((sysGetTicks(TIMER0) - btime) >uValue)
		        {
		            break;
		        }
		    }
	    	// Restore prescale value
	    	if(usePortC)
		    	outpw(REG_KPI_CONF, KPICONF_VALUE & ~0x80000);
		    else
		    	outpw(REG_KPI_CONF, KPICONF_VALUE);	
         #if 0 
			// Set GPIO as KPI function
		    #ifdef KPI_1PORT
		    nRegisterValue=nRegisterValue|GPIO_CFG_VALUE;
		    outpw(GPIO_CFG,nRegisterValue);
		    #endif

		    #ifdef KPI_2PORT
		    nRegisterValue=nRegisterValue|GPIO2_CFG_VALUE;
		    outpw(GPIO2_CFG,nRegisterValue);
		    #endif
         #endif
        	break;
        }
        default:
        {
            return kpiInvalidIoctlCommand;
            break;
        }
    }
    return Successful;
}


/****************************************************************************
* FUNCTION
*   kpiEnque
*
* DESCRIPTION
*   This function save the data to queue(KPI buffer)
*
* CALLED BY
*   kpiISR
*
* INPUTS
*   uValue       The data which need to save
*
* OUTPUTS
*   None
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/22/05	Created initial version 1.0
*
* REMARK
*     If the queue is full, the data will be discarded silently
****************************************************************************/
static VOID KPI_Enque(CONST UINT uValue)
{
    if(KPI_CheckFullQue() == FALSE)
    {
       kpiBuffer[nKPIBufferRear].value=uValue;
       nKPIBufferRear=(nKPIBufferRear+1)%KPI_QUE_SIZE;
    }
}

/****************************************************************************
* FUNCTION
*   kpiDeque
*
* DESCRIPTION
*   This function return the first data of queue(KPI buffer)
*
* CALLED BY
*   kpiRead
*
* INPUTS
*   None
*
* OUTPUTS
*   uValue      The data of queue(KPI buffer)
*   NOKEY       queue is empty
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/22/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static UINT KPI_Deque(VOID)
{
    UINT uValue;
    if(KPI_CheckEmptyQue() == FALSE)
    {
       uValue=kpiBuffer[nKPIBufferFront].value;
       nKPIBufferFront=(nKPIBufferFront+1)%KPI_QUE_SIZE;
       return uValue;
    }
    return NOKEY; //queue is empty
}

/****************************************************************************
* FUNCTION
*   kpiCheckFullQue
*
* DESCRIPTION
*   This function ckeck the queue status. The return value should be TURE if queue is full.
*
* CALLED BY
*   kpiEnque
*
* INPUTS
*   None
*
* OUTPUTS
*   TURE/FALSE       queue is full / queue is not full
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/22/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static BOOL KPI_CheckFullQue(VOID)
{
    if((nKPIBufferRear+1)%KPI_QUE_SIZE == nKPIBufferFront)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

/****************************************************************************
* FUNCTION
*   kpiCheckEmptyQue
*
* DESCRIPTION
*   This function ckeck the queue status. The return value should be TURE if queue is empty.
*
* CALLED BY
*   kpiRead
*   kpiDeque
*
* INPUTS
*   None
*
* OUTPUTS
*   TURE/FALSE       queue is empty / queue is not empty
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/22/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static BOOL KPI_CheckEmptyQue(VOID)
{
    if(nKPIBufferFront == nKPIBufferRear)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}
